using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using Altova.Types;
using Altova.Mapforce;


namespace Altova.Db
{
	public class TParameter
	{
		public TParameter(System.Data.DbType t) { type = t; }
		public TParameter(System.Data.DbType t, int l, int p, int s) { type = t; scale = s; precision = p; length = l; }
		public System.Data.DbType type;
		public int scale = -1;
		public int precision = -1;
		public int length = -1;
	}

	public class Record
	{
		public Dictionary<string, IEnumerable> fields   = new Dictionary<string, IEnumerable>();
        public Dictionary<string, IEnumerable> children = new Dictionary<string, IEnumerable>();
	}
	
	public class Recordset
	{
        public IDataReader reader;
		private Catalog catalog;
        private IDbConnection connection;

		public Recordset(Catalog catalog, IDataReader reader, IDbConnection conn)
		{
			this.catalog = catalog;
			this.reader = reader;
            this.connection = conn;
		}

        public void Close()
        {
            reader.Close();
			reader.Dispose();
            catalog.FreeConnection(connection);
        }
	}

	public class Statement
	{
		private IDbCommand command = null;
		private Catalog catalog= null;
		private List<TParameter> parameters = new List<TParameter>();
		private List<object> values = new List<object>();

		public Statement(Catalog cat, string statement)
		{
			catalog = cat;
			command = cat.MainConnection.CreateCommand();
			command.CommandText = statement;
			command.CommandType = CommandType.Text;
		}

		public IDbCommand Command { get { return command; } }
        public IDbConnection Connection
        {
            get
            {
                return command.Connection;
            }
            set
            {
                if (command.Connection != value)
                {
                    ICloneable c = command as ICloneable;
                    if (c == null)
                        throw new InvalidOperationException("Need to use a cloneable command");
                    command = (IDbCommand)c.Clone();
                    command.Connection = value;
                }
            }
        }
		public void AddParameter(TParameter par) { parameters.Add(par); }
		public void BindParameter(object val) { values.Add(val); }
		public void BindParameter(object val, int len, int precision, int scale)
        {
            values.Add(val);
            int i = values.Count - 1;
            parameters[i].length = len;
            parameters[i].precision = precision;
            parameters[i].scale = scale;
        }
		public void Clear() { values.Clear(); }
		public void Prepare()
		{
			foreach (TParameter p in parameters)
			{
				IDbDataParameter par = command.CreateParameter();
				par.DbType = p.type;
                if (p.type == DbType.Decimal && p.precision < 0) p.precision = 1;
				if (p.precision > 0) par.Precision = (byte) p.precision;
                if (p.type == DbType.Decimal && p.scale < 0) p.scale = 0;
				if (p.scale > 0) par.Scale = (byte) p.scale;
                par.Size = (p.length > 0) ? p.length : 2000;

				command.Parameters.Add(par);
			}
			command.Prepare();
		}

		private void PreExecute()
		{
			for (int i = 0; i < values.Count; i++)
            {
                TParameter p = parameters[i];
                IDbDataParameter db = ((IDbDataParameter)command.Parameters[i]);
                if (p.precision > 0)
                    db.Precision = (byte)p.precision;
                if (p.scale > 0)
                    db.Scale = (byte)p.scale;
                if (p.length > 0 && p.length > db.Size)
                    db.Size = p.length;

                db.Value = values[i] != null ? values[i] : DBNull.Value;
            }

			command.Transaction = catalog.CurrentTransaction;
		}

		public Recordset Execute()
		{
            PreExecute();
            
            IDataReader reader = command.ExecuteReader();
            while (reader.IsClosed && reader.NextResult()) {}

			return new Recordset(catalog, reader, command.Connection);
		}
		
		public int ExecuteNonReader()
		{
            PreExecute();
            return command.ExecuteNonQuery();
		}
	}
	
	public class Catalog
	{
		private IDbConnection mainConnection = null;
		private Dictionary<int, Statement> map = new Dictionary<int, Statement>();
        private bool visible = true;
        private bool inUse = false;
		private IDbTransaction currentTransaction = null;
		private bool targetCatalog = false;
		
		public Catalog(IDbConnection connection, bool isTarget)
		{
			mainConnection = connection;
			targetCatalog = isTarget;
		}
		
		public Catalog(IDbConnection connection)
		{
			mainConnection = connection;
		}

		public void Close()
        {
            visible = false;
            if (!inUse) 
                mainConnection.Close();
        }

        public IDbConnection AllocateConnection()
        {
			if (targetCatalog)
				return mainConnection;
				
            if (inUse)
            {
                ICloneable cloneable = mainConnection as ICloneable;
                if (cloneable == null)
                    throw new InvalidOperationException("Need to use a cloneable connection");

                IDbConnection use = (IDbConnection)cloneable.Clone();
                use.Open();
                return use;
            }
            else
            {
                inUse = true;
                return mainConnection;
            }
        }

        public void FreeConnection(IDbConnection conn)
        {
            if (conn == mainConnection)
            {
                inUse = false;
                if (!visible)
                    mainConnection.Close();
            }
            else
                conn.Close();
        }

		public IDbConnection MainConnection { get { return mainConnection; } }

		public Statement GetStatement(int id) { return map[id]; }

		public Statement CreateStatement(int id, string statement_string)
		{
			Statement statement = new Statement(this, statement_string);
			map[id] = statement;
			return statement;
		}
		
		public IDbTransaction CurrentTransaction
        {
            get { return currentTransaction; }
            set { currentTransaction = value; }
        }
	}

	public class TransactionHelper
		{
			string beginTrans;
			string commitTrans;
			string rollbackTrans;
			string savepoint;
			string rollbackSavepoint;
			IDbConnection connection;
			IDbTransaction transaction;
			int depth;
			
			public IDbTransaction Transaction { get { return transaction; } }
			
			void ExecuteCommandString(string s)
			{
				IDbCommand comm = connection.CreateCommand();
				comm.CommandText = s;
				comm.Transaction = Transaction;
				comm.ExecuteNonQuery();
			}
			
			void ExecuteCommandString(string s, string replaceName)
			{
				ExecuteCommandString(s.Replace(" %%TRANSACTION_NAME%% ", replaceName));
			}
			
			public TransactionHelper(IDbConnection conne, string begin, string commit,	string rollback,
				string save, string rollbackSave)
			{
				connection = conne;
				beginTrans = begin;
				commitTrans = commit;
				rollbackTrans = rollback;
				savepoint = save;
				rollbackSavepoint = rollbackSave;
				depth = 0;
			}
			
			public void BeginTrans(string transName)
			{
				if (depth == 0)
				{
					if (beginTrans.Length == 0)
						transaction = connection.BeginTransaction();
					else
						ExecuteCommandString(beginTrans);
				}
				else
				{
					if (savepoint.Length != 0)
						ExecuteCommandString(savepoint, transName);
				}
				++depth;
			}
			
			public void CommitTrans()
			{
				--depth;
				if (depth == 0)
				{
					if (beginTrans.Length == 0)
						transaction.Commit();
					else
						ExecuteCommandString(commitTrans);
				}
			}
			
			public void RollbackTrans(string transName)
			{
				--depth;
				if (depth == 0)
				{
					if (beginTrans.Length == 0)
						transaction.Rollback();
					else
						ExecuteCommandString(rollbackTrans);
				}
				else
				{
					if (savepoint.Length != 0)
						ExecuteCommandString(rollbackSavepoint, transName);
				}
			}
		}
		
		public class TransactionSentinel : System.IDisposable
		{
			private TransactionHelper  tr;
			string name;
			
			public TransactionSentinel (TransactionHelper t, string name)
			{
				tr = t;
				this.name = name;
				tr.BeginTrans(name);
			}
			
			public void Commit()
			{
				tr.CommitTrans();
				tr = null;
			}
			
			public void Rollback()
			{
				if (tr != null)
					tr.RollbackTrans(name);
			}
			
			public void Dispose()
			{
				Rollback();
			}
		}

	public class Dbs
	{

		static public void WriteField(Record record, string field, IEnumerable value)
		{
			record.fields[field] = value;
		}

		static public int ExecuteNonReader(Statement statement)
		{
			return statement.ExecuteNonReader();
		}
		
		static public void AddChildren(Record record, string field, IEnumerable children)
		{
			record.children[field] = children;
		}
		
		static public IEnumerable GetChildren(Record record, string field)
		{
			return record.children[field];
		}
		
		static public void  BindParameter_empty(Statement command)
		{
			command.BindParameter(null, -1, -1, -1);
		}
		
		static public void BindParameter_string(Statement command, string value)
		{
			command.BindParameter(value, value.Length, -1, -1);
		}
		
		static public IEnumerable ReadField(Record record, string name)
		{
			return record.fields[name];
		}
		
		static public void BindParameter_decimal(Statement command, decimal value)
		{
			int precision = 0;
            int scale = 0;
            bool inFraction = false;
            bool nonZeroSeen = false;

            string s = value.ToString(System.Globalization.CultureInfo.InvariantCulture);

            foreach (char c in s)
            {
                if (inFraction)
                {
                    if (c != '0')
                        nonZeroSeen = true;

                    precision++;
                    scale++;
                }
                else
                {
                    if (c == '.')
                    {
                        inFraction = true;
                    }
                    else if (c != '-')
                    {
                        if (c != '0' || nonZeroSeen)
                        {
                            nonZeroSeen = true;
                            precision++;
                        }
                    }
                }
            }

            // Handles cases where all digits are zeros.
            if (!nonZeroSeen)
                precision += 1;

			command.BindParameter((object)value, -1, precision, scale);
		}
		
		static public void BindParameter_DateTime(Statement command, Altova.Types.DateTime value)
		{
			command.BindParameter(value.Value);
		}
		
		static public void BindParameter_TimeSpan(Statement command, Altova.Types.DateTime value)
		{
            System.DateTime dt = value.Value; // extract time from datetime
            System.TimeSpan ts = new System.TimeSpan( 0, dt.Hour, dt.Minute, dt.Second, dt.Millisecond );
			BindParameter_TimeSpan( command, ts );
		}

		static public void BindParameter_TimeSpan(Statement command, System.TimeSpan value)
		{
			command.BindParameter(value);
		}

        static public void BindParameter_long(Statement command, long value)
		{
			command.BindParameter(value);
		}
		
		static public void BindParameter_ulong(Statement command, ulong value)
		{
			command.BindParameter(value);
		}
		
		static public void BindParameter_int(Statement command, int value)
		{
			command.BindParameter(value);
		}

		static public void BindParameter_double(Statement command, double value)
		{
			command.BindParameter(value);
		}
		
		static public void BindParameter_bool(Statement command, bool value)
		{
			command.BindParameter(value);
		}
		
		static public void BindParameter_binary(Statement command, byte[] value)
		{
			command.BindParameter(value, value.Length, -1, -1);
		}

        static public void BindParameter_Money_PostgreSQL(Statement command, double value)
        {
            command.BindParameter(value.ToString(System.Globalization.CultureInfo.InvariantCulture));
        }

        static public void BindParameter_Uniqueidentifier_SQLServer(Statement command, string value)
        {
            command.BindParameter(value != null ? (object)(new System.Guid(value)) : null);
        }

        static public void BindParameter_IntervalDaySecond_Oracle(Statement command, Duration d)
        {
            String s = d.IsNegative() ? "-" : "+";
            TimeSpan ts = d.IsNegative() ? d.Value.Negate() : d.Value;

            s += ts.Days;
            s += " ";

            s += ts.Hours;
            s += ":";
            s += ts.Minutes;
            s += ":";
            s += ts.Seconds;

            double partsecond = (System.Math.Abs(d.Value.Ticks) / 10000000.0) % 1.0;
            if (partsecond > 0.0 && partsecond < 1.0)
            {
                string sPartSecond = partsecond.ToString("0.##########");
                s += "." + sPartSecond.Substring(2, sPartSecond.Length - 2);
            }

            command.BindParameter(s, s.Length, 0, 0);
        }

        static public void BindParameter_IntervalYearMonth_Oracle(Statement command, Duration d)
        {
            String s = d.IsNegative() ? "-" : "+";

            s += System.Math.Abs(d.Years);
            s += "-";
            s += System.Math.Abs(d.Months);

            command.BindParameter(s, s.Length, 0, 0);
        }

		static public void BindParameter_Interval_PostgreSQL(Statement command, Duration d)
        {
            String s = d.IsNegative() ? "-" : "+";
            TimeSpan ts = d.IsNegative() ? d.Value.Negate() : d.Value;

            s += System.Math.Abs(d.Years);
            s += "-";
            s += System.Math.Abs(d.Months);
            s += "-";
            s += ts.Days;
            s += " ";

            s += ts.Hours;
            s += ":";
            s += ts.Minutes;
            s += ":";
            s += ts.Seconds;

            double partsecond = (System.Math.Abs(d.Value.Ticks) / 10000000.0) % 1.0;
            if (partsecond > 0.0 && partsecond < 1.0)
            {
                string sPartSecond = partsecond.ToString("0.##########");
                s += "." + sPartSecond.Substring(2, sPartSecond.Length - 2);
            }

            command.BindParameter(s, s.Length, 0, 0);
        }

        static public void AddParameter(Statement statement, TParameter p)
		{
			statement.AddParameter(p);
		}

		static public Statement NewQuery(Catalog catalog, int id)
		{
			Statement statement = catalog.GetStatement(id);
			statement.Connection = catalog.AllocateConnection();
			statement.Clear();
			return statement;
		}

		static public Statement NewStatement(Catalog catalog, int id, string statement)
		{
			return catalog.CreateStatement(id, statement);
		}

		static public void StoreStatement(Statement statement)
		{
		}
		
		static public void PrepareStatement(Statement statement)
		{
			statement.Prepare();
		}

		static public Recordset ExecuteQuery(Statement command)
		{
			return command.Execute();
		}

		static public Record NewRecord()
		{
			return new Record();
		}

		static public bool MoveNextRecord(Recordset recordset)
		{
			return recordset.reader.Read();
		}

		static public void DisposeRecordset(Recordset recordset)
		{
            if (recordset != null)
    			recordset.Close();
		}

		static public void DisposeQuery(Statement query)
		{
			query.Clear();
		}


		// KBFs

		static public void Read_string(Record record, Recordset recordset, string name)
		{
			int col = recordset.reader.GetOrdinal(name);
			if (recordset.reader.IsDBNull(col))
			{
				record.fields[name] = MFEmptySequence.Instance;
				return;
			}

			record.fields[name] = new MFSingletonSequence(recordset.reader.GetString(col));
		}
		
		static public void Read_sbyte(Record record, Recordset recordset, string name)
		{
			int col = recordset.reader.GetOrdinal(name);
			if (recordset.reader.IsDBNull(col))
			{
				record.fields[name] = MFEmptySequence.Instance;
				return;
			}

            int value = System.Convert.ToInt32(recordset.reader.GetValue(col));	
			record.fields[name] = new MFSingletonSequence(value);
		}

		static public void Read_byte(Record record, Recordset recordset, string name)
		{
			int col = recordset.reader.GetOrdinal(name);
			if (recordset.reader.IsDBNull(col))
			{
				record.fields[name] = MFEmptySequence.Instance;
				return;
			}

			ulong value = System.Convert.ToUInt64(recordset.reader.GetValue(col));
			record.fields[name] = new MFSingletonSequence(value);
		}

		static public void Read_short(Record record, Recordset recordset, string name)
		{
			int col = recordset.reader.GetOrdinal(name);
			if (recordset.reader.IsDBNull(col))
			{
				record.fields[name] = MFEmptySequence.Instance;
				return;
			}
			int value = System.Convert.ToInt32(recordset.reader.GetValue(col));
			record.fields[name] = new MFSingletonSequence(value);
		}

		static public void Read_ushort(Record record, Recordset recordset, string name)
		{
			int col = recordset.reader.GetOrdinal(name);
			if (recordset.reader.IsDBNull(col))
			{
				record.fields[name] = MFEmptySequence.Instance;
				return;
			}
			ulong value = System.Convert.ToUInt64(recordset.reader.GetValue(col));	
			record.fields[name] = new MFSingletonSequence(value);
		}

		static public void Read_int(Record record, Recordset recordset, string name)
		{
			int col = recordset.reader.GetOrdinal(name);
			if (recordset.reader.IsDBNull(col))
			{
				record.fields[name] = MFEmptySequence.Instance;
				return;
			}
			
			int value = System.Convert.ToInt32(recordset.reader.GetValue(col));
			record.fields[name] = new MFSingletonSequence(value);
		}

		static public void Read_uint(Record record, Recordset recordset, string name)
		{
			int col = recordset.reader.GetOrdinal(name);
			if (recordset.reader.IsDBNull(col))
			{
				record.fields[name] = MFEmptySequence.Instance;
				return;
			}
			
			ulong value = System.Convert.ToUInt64(recordset.reader.GetValue(col));
			record.fields[name] = new MFSingletonSequence(value);
		}

		static public void Read_long(Record record, Recordset recordset, string name)
		{
			int col = recordset.reader.GetOrdinal(name);
			if (recordset.reader.IsDBNull(col))
			{
				record.fields[name] = MFEmptySequence.Instance;
				return;
			}

			long value = System.Convert.ToInt64(recordset.reader.GetValue(col));
			record.fields[name] = new MFSingletonSequence(value);
		}

		static public void Read_ulong(Record record, Recordset recordset, string name)
		{
			int col = recordset.reader.GetOrdinal(name);
			if (recordset.reader.IsDBNull(col))
			{
				record.fields[name] = MFEmptySequence.Instance;
				return;
			}

			ulong value = System.Convert.ToUInt64(recordset.reader.GetValue(col));
			record.fields[name] = new MFSingletonSequence(value);
		}

		static public void Read_float(Record record, Recordset recordset, string name)
		{
			int col = recordset.reader.GetOrdinal(name);
			if (recordset.reader.IsDBNull(col))
			{
				record.fields[name] = MFEmptySequence.Instance;
				return;
			}

			record.fields[name] = new MFSingletonSequence((float)recordset.reader.GetValue(col));
		}

		static public void Read_double(Record record, Recordset recordset, string name)
		{
			int col = recordset.reader.GetOrdinal(name);
			if (recordset.reader.IsDBNull(col))
			{
				record.fields[name] = MFEmptySequence.Instance;
				return;
			}

            double value = 0.0;
            object o = recordset.reader.GetValue(col);
            if (o is double) { value = (double)(o); }
            else if (o is float) { value = (float)(o); }
            else { /* type */ }
			record.fields[name] = new MFSingletonSequence(value);
		}

		static public void Read_decimal(Record record, Recordset recordset, string name)
		{
			int col = recordset.reader.GetOrdinal(name);
			if (recordset.reader.IsDBNull(col))
			{
				record.fields[name] = MFEmptySequence.Instance;
				return;
			}

			decimal value = 0m;
            object o = recordset.reader.GetValue(col);
            if (o is double)
                value = (decimal)(double)o;
            else
                value = (decimal)o;
			record.fields[name] = new MFSingletonSequence(value);
		}

		static public void Read_DateTime(Record record, Recordset recordset, string name)
		{
			int col = recordset.reader.GetOrdinal(name);
			if (recordset.reader.IsDBNull(col))
			{
				record.fields[name] = MFEmptySequence.Instance;
				return;
			}
			record.fields[name] = new MFSingletonSequence(new Altova.Types.DateTime((System.DateTime)recordset.reader.GetDateTime(col)));
		}

		static public void Read_bool(Record record, Recordset recordset, string name)
		{
			int col = recordset.reader.GetOrdinal(name);
			if (recordset.reader.IsDBNull(col))
			{
				record.fields[name] = MFEmptySequence.Instance;
				return;
			}

			record.fields[name] = new MFSingletonSequence((bool)recordset.reader.GetBoolean(col));
		}

		static public void Read_blob(Record record, Recordset recordset, string name)
		{
			int col = recordset.reader.GetOrdinal(name);
			if (recordset.reader.IsDBNull(col))
			{
				record.fields[name] = MFEmptySequence.Instance;
				return;
			}

			long l = recordset.reader.GetBytes(col, 0, null, 0, 0);
			byte[] b = new byte[l];
			try
			{
				if(l > 0) recordset.reader.GetBytes(col, 0, b, 0, (int)l);
			}
			catch (System.InvalidCastException)
			{
				// Workaround for DB2 NULL values in XML. TODO is this needed?
				record.fields[name] = MFEmptySequence.Instance;
				return;
			}

			record.fields[name] = new MFSingletonSequence(b);
		}

        // special handling

        static public void Read_blob_XML_DB2(Record record, Recordset recordset, string name)
		{
            try
            {
                // avoid ERROR: Unknown SQL type - -98.
			    int col = recordset.reader.GetOrdinal(name);
                long l = recordset.reader.GetBytes(col, 0, null, 0, 0);

                if (l <= 0)
			    {
				    record.fields[name] = MFEmptySequence.Instance;
			    }
                else
                {
			        byte[] b = new byte[l];
                    recordset.reader.GetBytes(col, 0, b, 0, (int)l);
			        record.fields[name] = new MFSingletonSequence(b);
                }
            }
            catch (System.InvalidCastException)
            {
	            record.fields[name] = MFEmptySequence.Instance;
	            return;
            }
		}


        static public void Read_string_ParseDate(Record record, Recordset recordset, string name)
        {
            int col = recordset.reader.GetOrdinal(name);
            if (recordset.reader.IsDBNull(col))
            {
                record.fields[name] = MFEmptySequence.Instance;
                return;
            }

            string value = recordset.reader.GetString(col);
            Altova.Types.DateTimeFormat format = Altova.Types.DateTimeFormat.W3_date;
            Altova.Types.DateTime dt = Altova.Types.DateTime.Parse( value, format);
            record.fields[name] = new MFSingletonSequence( dt );
        }

        static public void Read_string_ParseTime(Record record, Recordset recordset, string name)
        {
            int col = recordset.reader.GetOrdinal(name);
            if (recordset.reader.IsDBNull(col))
            {
                record.fields[name] = MFEmptySequence.Instance;
                return;
            }

            string value = recordset.reader.GetString(col);
            Altova.Types.DateTimeFormat format = Altova.Types.DateTimeFormat.W3_time;
            Altova.Types.DateTime dt = Altova.Types.DateTime.Parse(value, format);
            record.fields[name] = new MFSingletonSequence(dt);
        }

        static public void Read_string_ParseTimeTZ_PostgreSQL(Record record, Recordset recordset, string name)
        {
            int col = recordset.reader.GetOrdinal(name);
            if (recordset.reader.IsDBNull(col))
            {
                record.fields[name] = MFEmptySequence.Instance;
                return;
            }

            string value = recordset.reader.GetString(col);
            //Altova.Types.DateTimeFormat format = Altova.Types.DateTimeFormat.W3_time;
            //Altova.Types.DateTime dt = Altova.Types.DateTime.Parse(value, format);
            Altova.Types.DateTime.DateTimePart part = Altova.Types.DateTime.DateTimePart.Time;
            Altova.Types.DateTime.DateTimePart optional = Altova.Types.DateTime.DateTimePart.TimezoneMinute;
            Altova.Types.DateTime dt = new Altova.Types.DateTime();
            bool valid = dt.ParseDateTime(value, part, optional);
            record.fields[name] = new MFSingletonSequence(dt);
        }

        static public void Read_string_ParseDateTime(Record record, Recordset recordset, string name)
        {
            int col = recordset.reader.GetOrdinal(name);
            if (recordset.reader.IsDBNull(col))
            {
                record.fields[name] = MFEmptySequence.Instance;
                return;
            }

            string value = recordset.reader.GetString(col);
            Altova.Types.DateTimeFormat format = Altova.Types.DateTimeFormat.W3_dateTime;
            Altova.Types.DateTime dt = Altova.Types.DateTime.Parse(value, format);
            record.fields[name] = new MFSingletonSequence(dt);
        }

        static public void Read_string_ParseDecimal(Record record, Recordset recordset, string name)
        {
            int col = recordset.reader.GetOrdinal(name);
            if (recordset.reader.IsDBNull(col))
            {
                record.fields[name] = MFEmptySequence.Instance;
                return;
            }

            // remove trailing zeros to avoid 'invalid decimal'
            String value = recordset.reader.GetString(col);
            while (value.EndsWith("0") && value.Contains("."))
                value = value.Remove(value.Length - 1);

            Decimal native = Altova.CoreTypes.CastToDecimal(value);
            record.fields[name] = new MFSingletonSequence( native );
        }

        static public void Read_string_ParseDouble(Record record, Recordset recordset, string name)
        {
            int col = recordset.reader.GetOrdinal(name);
            if (recordset.reader.IsDBNull(col))
            {
                record.fields[name] = MFEmptySequence.Instance;
                return;
            }

            // remove trailing zeros to avoid 'invalid decimal'
            String value = recordset.reader.GetString(col);
            while (value.EndsWith("0") && value.Contains("."))
                value = value.Remove(value.Length - 1);

            Double native = Altova.CoreTypes.CastToDouble(value);
            record.fields[name] = new MFSingletonSequence( native );
        }

        static public void Read_blob_Bitfield_MySQL(Record record, Recordset recordset, string name)
        {
            int col = recordset.reader.GetOrdinal(name);
            if (recordset.reader.IsDBNull(col))
            {
                record.fields[name] = MFEmptySequence.Instance;
                return;
            }

            ulong value = 0;
            object o = recordset.reader.GetValue(col);
            if (o is short) value = (ushort)(short)(o); 
            else if (o is byte[]) 
            {
                int length = 8;
                byte[] buffer = new byte[length];
                long size = recordset.reader.GetBytes(col, 0, buffer, 0, length);
                for( long i = 0; i < size; ++i )
                {
                    byte n = buffer[i];
                    value = value << 8;
                    value = value | n;
                }
            }
            record.fields[name] = new MFSingletonSequence( value );
        }

        static public void Read_string_ParseIntervalYearToMonth_Oracle(Record record, Recordset recordset, string name)
        {
            int col = recordset.reader.GetOrdinal(name);
            if (recordset.reader.IsDBNull(col))
            {
                record.fields[name] = MFEmptySequence.Instance;
                return;
            }

            int n = 0;
            string s = recordset.reader.GetString(col);
            string e = s + " cannot be converted to a duration value";
            Altova.Types.DateTime.ParseContext context = new Altova.Types.DateTime.ParseContext(s.Trim());

            bool positive = true; // parse sign
            if( context.CheckAndAdvance('-') ) positive = false;
            else if( context.CheckAndAdvance('+') ) positive = true;
            else throw new StringParseException(e + "Invalid or missing sign.");

            int years = 0; // parse year component
            if (!context.IsValid()) throw new StringParseException(e + "Missing year value.");
            while (context.ReadDigitAndAdvance(ref n, 1, 9)) { years = (years * 10) + n; n = 0; }

            // check year-month divider
            if (!context.IsValid()) throw new StringParseException(e + "Missing year-month divider.");
            if (!context.CheckAndAdvance('-')) throw new StringParseException(e + "Invalid year-month divider.");

            int month = 0; // parse month component
            if (!context.IsValid()) throw new StringParseException(e + "Missing month value.");
            while (context.ReadDigitAndAdvance(ref n, 1, 9)) { month = (month * 10) + n; n = 0;  }

            Duration duration = new Duration(years, month, 0, 0, 0, 0, 0.0, !positive );
            record.fields[name] = new MFSingletonSequence( duration );
        }

        static public void Read_string_ParseIntervalDayToSecond_Oracle(Record record, Recordset recordset, string name)
        {
            int col = recordset.reader.GetOrdinal(name);
            if (recordset.reader.IsDBNull(col))
            {
                record.fields[name] = MFEmptySequence.Instance;
                return;
            }

            int n = 0;
            string s = recordset.reader.GetString(col);
            string e = s + " cannot be converted to a duration value";
            Altova.Types.DateTime.ParseContext context = new Altova.Types.DateTime.ParseContext(s.Trim());

            bool positive = true;// parse sign
            if (context.CheckAndAdvance('-')) positive = false;
            else if (context.CheckAndAdvance('+')) positive = true;
            else throw new StringParseException(e);
            if (!context.IsValid()) throw new StringParseException(e);

            int days = 0;// parse days component
            while( context.ReadDigitAndAdvance(ref n, 1, 9) ) { days = (days*10) + n; n = 0; }
            if (!context.IsValid()) throw new StringParseException(e);

            // check days-time divider
            if (!context.CheckAndAdvance(' ')) throw new StringParseException(e);
            if (!context.IsValid()) throw new StringParseException(e);

            string isotime = s.Substring( context.Index );// parse hours:minutes:seconds.fraction 
            Altova.Types.DateTime time = Altova.Types.DateTime.Parse(isotime, DateTimeFormat.W3_time);
            System.DateTime value = time.Value;

            Duration duration = new Duration(0, 0, days, value.Hour, value.Minute, value.Second, value.Millisecond / 1000.0, !positive);
            record.fields[name] = new MFSingletonSequence(duration);
        }

        static public void Read_Unicode_PostgreSQL(Record record, Recordset recordset, string name)
        {
            try
            {
                // an InvalidCastException is raised for DBNull values
                int col = recordset.reader.GetOrdinal(name);
                if (!recordset.reader.IsDBNull(col))
                {
                    long l = recordset.reader.GetBytes(col, 0, null, 0, 0);
                    if (l >= 0)
                    {
                        byte[] chars = new byte[l];
                        if (l > 0) recordset.reader.GetBytes(col, 0, chars, 0, (int)l);
                        System.Text.Encoding encoding = System.Text.Encoding.UTF8; // fix
                        record.fields[name] = new MFSingletonSequence(encoding.GetString(chars));
                        return;
                    }
                }
            }
            catch (System.InvalidCastException)
            {
            }

            record.fields[name] = MFEmptySequence.Instance;
        }

        static public void Read_string_ParseInterval_PostgreSQL(Record record, Recordset recordset, string name)
        {
            int col = recordset.reader.GetOrdinal(name);
            if (recordset.reader.IsDBNull(col))
            {
                record.fields[name] = MFEmptySequence.Instance;
                return;
            }

            // Format: YYYY/MM/DD/hh/mm/ss.ff9

            bool positive = true;
            int years = 0;
            int month = 0;
            int days = 0;
            int hours = 0;
            int minutes = 0;
            int seconds = 0;
            double fraction = 0.0;

            int n = 0;
            string s = recordset.reader.GetString(col);
            string e = s + " cannot be converted to a duration value";
            Altova.Types.DateTime.ParseContext context = new Altova.Types.DateTime.ParseContext(s.Trim());

            if (context.CheckAndAdvance('-')) positive = false;
            if (!context.IsValid()) throw new StringParseException(e);
            while (context.ReadDigitAndAdvance(ref n, 1, 9)) { years = (years * 10) + n; n = 0; }
            if (!context.IsValid()) throw new StringParseException(e);

            if (!context.CheckAndAdvance('/')) throw new StringParseException(e);

            if (context.CheckAndAdvance('-')) positive = false;
            if (!context.IsValid()) throw new StringParseException(e);
            while (context.ReadDigitAndAdvance(ref n, 1, 9)) { month = (month * 10) + n; n = 0; }
            if (!context.IsValid()) throw new StringParseException(e);

            if (!context.CheckAndAdvance('/')) throw new StringParseException(e);

            if (context.CheckAndAdvance('-')) positive = false;
            if (!context.IsValid()) throw new StringParseException(e);
            while (context.ReadDigitAndAdvance(ref n, 1, 9)) { days = (days * 10) + n; n = 0; }
            if (!context.IsValid()) throw new StringParseException(e);

            if (!context.CheckAndAdvance('/')) throw new StringParseException(e);

            if (context.CheckAndAdvance('-')) positive = false;
            if (!context.IsValid()) throw new StringParseException(e);
            while (context.ReadDigitAndAdvance(ref n, 1, 9)) { hours = (hours * 10) + n; n = 0; }
            if (!context.IsValid()) throw new StringParseException(e);

            if (!context.CheckAndAdvance('/')) throw new StringParseException(e);

            if (context.CheckAndAdvance('-')) positive = false;
            if (!context.IsValid()) throw new StringParseException(e);
            while (context.ReadDigitAndAdvance(ref n, 1, 9)) { minutes = (minutes * 10) + n; n = 0; }
            if (!context.IsValid()) throw new StringParseException(e);

            if (!context.CheckAndAdvance('/')) throw new StringParseException(e);

            if (context.CheckAndAdvance('-')) positive = false;
            if (!context.IsValid()) throw new StringParseException(e);
            while (context.ReadDigitAndAdvance(ref n, 1, 9)) { seconds = (seconds * 10) + n; n = 0; }
            if (!context.IsValid()) throw new StringParseException(e);
            if (context.CheckAndAdvance('.')) 
            {
                int temp = 0; // micoseconds
                if (!context.IsValid()) throw new StringParseException(e);
                while (context.ReadDigitAndAdvance(ref n, 1, 9)) { temp = (temp * 10) + n; n = 0; }
                fraction = temp / 1000000.0; 
            }

            Duration duration = new Duration(years, month, days, hours, minutes, seconds, fraction, !positive);
            record.fields[name] = new MFSingletonSequence(duration);
        }

        static public void Read_guid_BuildString(Record record, Recordset recordset, string name)
        {
            int col = recordset.reader.GetOrdinal(name);
            if (recordset.reader.IsDBNull(col))
            {
                record.fields[name] = MFEmptySequence.Instance;
                return;
            }

            System.Guid guid = recordset.reader.GetGuid(col);
            string format = "B";//separated by hyphens, enclosed in brackets
            String value = guid.ToString(format).ToUpper();
            record.fields[name] = new MFSingletonSequence(value);
        }

        static public void Read_string_BuildGuidString(Record record, Recordset recordset, string name)
        {
            int col = recordset.reader.GetOrdinal(name);
            if (recordset.reader.IsDBNull(col))
            {
                record.fields[name] = MFEmptySequence.Instance;
                return;
            }

            string value = recordset.reader.GetString(col);
            if(!value.StartsWith("{")) value = "{" + value;
            if(!value.EndsWith("}")) value = value + "}";
            record.fields[name] = new MFSingletonSequence(value.ToUpper());
        }

        static public void Read_int_Boolean_PostgreSQL(Record record, Recordset recordset, string name)
        {
            int col = recordset.reader.GetOrdinal(name);
            if (recordset.reader.IsDBNull(col))
            {
                record.fields[name] = MFEmptySequence.Instance;
                return;
            }
            int value = recordset.reader.GetInt32(col);
            record.fields[name] = new MFSingletonSequence((bool)(value != 0));
		}

        static public void Read_TimeSpan_BuildTime(Record record, Recordset recordset, string name)
        {
            int col = recordset.reader.GetOrdinal(name);
            if (recordset.reader.IsDBNull(col))
            {
                record.fields[name] = MFEmptySequence.Instance;
                return;
            }
            System.TimeSpan ts = (System.TimeSpan)(recordset.reader.GetValue(col));
            System.DateTime dt = new System.DateTime(1,1,1,ts.Hours,ts.Minutes,ts.Seconds,ts.Milliseconds);
            record.fields[name] = new MFSingletonSequence(new Altova.Types.DateTime(dt));
        }


        static public void Read_blob_ROWID_iSeries(Record record, Recordset recordset, string name)
		{
			int col = recordset.reader.GetOrdinal(name);
			if (recordset.reader.IsDBNull(col))
			{
				record.fields[name] = MFEmptySequence.Instance;
				return;
			}

			long l = recordset.reader.GetBytes(col, 0, null, 0, 0);
			byte[] b = new byte[l];
            if (l > 0) recordset.reader.GetBytes(col, 0, b, 0, (int)l);

            string s = Altova.HexBinary.decode(b);
			record.fields[name] = new MFSingletonSequence(s);
		}

        static public void Read_DbClob_DB2(Record record, Recordset recordset, string name)
        {
            try
            {
                // don't call IsDBNull => Unknown SQL type - -97. 
                // an InvalidCastException is raised for DBNull values
                int col = recordset.reader.GetOrdinal(name);
                long l = recordset.reader.GetChars(col, 0, null, 0, 0);
                if( l >= 0 )
                {
                    char[] chars = new char[l];
                    if( l > 0 ) recordset.reader.GetChars(col, 0, chars, 0, (int)l);
                    record.fields[name] = new MFSingletonSequence(new String(chars));
                    return;
                }
            }
            catch (System.InvalidCastException)
            {
            }

            record.fields[name] = MFEmptySequence.Instance;
        }
    }
}